/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "los_interrupt.h"
#include <stdarg.h>
#include "securec.h"
#include "los_context.h"
#include "los_arch_interrupt.h"
#include "los_debug.h"
#include "los_hook.h"
#include "los_task.h"
#include "los_sched.h"
#include "los_memory.h"
#include "los_membox.h"

#include "los_mipsregs.h"
#include "los_cache.h"
#include "los_cpu.h"
 
extern int ffs(int mask);
extern void mips_general_exception(void);
extern void mips_handle_int(void);
extern void mips_handle_reserved(void);
extern void MIPS_CPU_SetSR(unsigned int , unsigned int );
extern LITE_OS_SEC_TEXT VOID __HalInterrupt(int irqnum, VOID *parm);

/*lint -save -e40 -e522 -e533*/
UINT32 g_intCount = 0;


// 中断配置寄存器
#define MIPS_INTREG_BASE                    (0xbfd01040)
#define MIPS_EXCEPTION_MAX_NUM                       (32)
#define MIPS_EXCEPTION_HANDER_MAX_SIZE               (0x80)
#define MIPS_NR_IRQS    (32*5)
#define MIPS_MAX_NUM                             (MIPS_NR_IRQS)

struct mips_intc_regs
{
	volatile unsigned int int_isr;
	volatile unsigned int int_en;
	volatile unsigned int int_set;
	volatile unsigned int int_clr;		/* offset 0x10*/
	volatile unsigned int int_pol;
   	volatile unsigned int int_edge;		/* offset 0 */
}; 
struct mips_intc_regs volatile *mips_hw0_icregs 
    = (struct mips_intc_regs volatile *)MIPS_INTREG_BASE;

// 异常处理函数
unsigned long mips_exception_handlers[MIPS_EXCEPTION_MAX_NUM];

// 中断处理函数
irq_desc_t mips_irq_handlers[MIPS_MAX_NUM];

STATIC HWI_PROC_FUNC __attribute__((aligned(0x100))) g_hwiForm[OS_VECTOR_CNT] = {0};

#if (OS_HWI_WITH_ARG == 1)

typedef struct {
    HWI_PROC_FUNC pfnHandler;
    VOID *pParm;
} HWI_HANDLER_FUNC;

/* *
 * @ingroup los_hwi
 * hardware interrupt handler form mapping handling function array.
 */
STATIC HWI_HANDLER_FUNC g_hwiHandlerForm[OS_VECTOR_CNT] = {{ (HWI_PROC_FUNC)0, (HWI_ARG_T)0 }};

/* *
 * @ingroup los_hwi
 * Set interrupt vector table.
 */
VOID OsSetVector(UINT32 num, HWI_PROC_FUNC vector, VOID *arg)
{
    if ((num + OS_SYS_VECTOR_CNT) < OS_VECTOR_CNT) {
        g_hwiForm[num + OS_SYS_VECTOR_CNT] = (HWI_PROC_FUNC)__HalInterrupt;
        g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT].pfnHandler = vector;
        g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT].pParm = arg;

    }
}

LITE_OS_SEC_TEXT_MINOR VOID HalHwiDefaultHandler(UINT32 num, VOID *arg)
{
    printf("%s irqnum\n", __FUNCTION__);
    while (1) {}
}
#else
/* *
 * @ingroup los_hwi
 * hardware interrupt handler form mapping handling function array.
 */
STATIC HWI_PROC_FUNC g_hwiHandlerForm[OS_VECTOR_CNT] = {0};

/* *
 * @ingroup los_hwi
 * Set interrupt vector table.
 */
VOID OsSetVector(UINT32 num, HWI_PROC_FUNC vector)
{
    if ((num + OS_SYS_VECTOR_CNT) < OS_VECTOR_CNT) {
        g_hwiForm[num + OS_SYS_VECTOR_CNT] = __HalInterrupt;
        g_hwiHandlerForm[num + OS_SYS_VECTOR_CNT] = vector;
    }
}

LITE_OS_SEC_TEXT_MINOR VOID HalHwiDefaultHandler(VOID)
{
    printf("%s irqnum\n", __FUNCTION__);
    while (1) {}
}
#endif



WEAK VOID HalPreInterruptHandler(UINT32 arg)
{
    return;
}

WEAK VOID HalAftInterruptHandler(UINT32 arg)
{
    return;
}

void mips_irq_enable(int IRQn)
{
    (mips_hw0_icregs + (IRQn >> 5))->int_en |= (1 << (IRQn & 0x1f));
    return ;
}


// 禁止滴答定时器
void mips_sys_tick_disable(void)
{
    unsigned int status = 0;

    status = read_c0_status();
    status &= (~STATUSF_IP7);
    write_c0_status(status);
    return ;
}


// 使能滴答定时器
void mips_sys_tick_enable(void)
{
    unsigned int status = 0;

    status = read_c0_status();
    status |= STATUSF_IP7;
    write_c0_status(status);
    return ;
}
/* ****************************************************************************
 Function    : HalHwiCreate
 Description : create hardware interrupt
 Input       : hwiNum   --- hwi num to create
               hwiPrio  --- priority of the hwi
               mode     --- unused
               handler --- hwi handler
               arg      --- param of the hwi handler
 Output      : None
 Return      : LOS_OK on success or error code on failure
 **************************************************************************** */
LITE_OS_SEC_TEXT_INIT UINT32 HalHwiCreate(HWI_HANDLE_T hwiNum,
                                          HWI_PRIOR_T hwiPrio,
                                          HWI_MODE_T mode,
                                          HWI_PROC_FUNC handler,
                                          HWI_ARG_T arg)
{
    hwiPrio = hwiPrio;

    if (handler == NULL) {
        return OS_ERRNO_HWI_PROC_FUNC_NULL;
    }
#if 1
    if (hwiNum >= OS_HWI_MAX_NUM) {
        return OS_ERRNO_HWI_NUM_INVALID;
    }
#endif
    if (g_hwiForm[hwiNum + OS_SYS_VECTOR_CNT] != (HWI_PROC_FUNC)HalHwiDefaultHandler) {
        return OS_ERRNO_HWI_ALREADY_CREATED;
    }

#if (OS_HWI_WITH_ARG == 1)
    OsSetVector(hwiNum, handler, arg);
#else
    OsSetVector(hwiNum, handler);
#endif

    if(hwiNum == LS1C_TICK_IRQ)
    {
        
    }else{
        mips_irq_enable(hwiNum);
    }


    
    return LOS_OK;
}


LITE_OS_SEC_TEXT_INIT UINT32 HalOSHwiCreate(INT32 hwiNum,
                                          HWI_PRIOR_T hwiPrio,
                                          HWI_MODE_T mode,
                                          HWI_PROC_FUNC handler,
                                          HWI_ARG_T arg)
{

    hwiPrio = hwiPrio;

    if (handler == NULL) {
        return OS_ERRNO_HWI_PROC_FUNC_NULL;
    }
#if 1
    if (hwiNum >= OS_HWI_MAX_NUM) {
        return OS_ERRNO_HWI_NUM_INVALID;
    }
#endif
    if (g_hwiForm[hwiNum + OS_SYS_VECTOR_CNT] != (HWI_PROC_FUNC)HalHwiDefaultHandler) {
        return OS_ERRNO_HWI_ALREADY_CREATED;
    }

#if (OS_HWI_WITH_ARG == 1)
    OsSetVector(hwiNum, handler, arg);
#else
    OsSetVector(hwiNum, handler);
#endif

    if(hwiNum == LS1C_TICK_IRQ)
    {
        //mips_sys_tick_enable();
    }else{
        mips_irq_enable(hwiNum);
    }
    
    return LOS_OK;
}

void mips_irq_disable(int IRQn)
{
    (mips_hw0_icregs + (IRQn >> 5))->int_en &= ~(1 << (IRQn & 0x1f));
    return ;
}

/* ****************************************************************************
 Function    : HalHwiDelete
 Description : Delete hardware interrupt
 Input       : hwiNum   --- hwi num to delete
 Output      : None
 Return      : LOS_OK on success or error code on failure
 **************************************************************************** */
LITE_OS_SEC_TEXT_INIT UINT32 HalHwiDelete(HWI_HANDLE_T hwiNum)
{
    
    if (hwiNum >= OS_HWI_MAX_NUM) {
        return OS_ERRNO_HWI_NUM_INVALID;
    }
    
    if(hwiNum == LS1C_TICK_IRQ)
    {
        //mips_sys_tick_disable();
    }else{
        mips_irq_disable(hwiNum);
    }

    g_hwiForm[hwiNum + OS_SYS_VECTOR_CNT] = (HWI_PROC_FUNC)HalHwiDefaultHandler;

    return LOS_OK;
}

extern void (*g_mips_tick_handle)(void);
/* ****************************************************************************
 Function    : HalIntNumGet
 Description : Get an interrupt number
 Input       : None
 Output      : None
 Return      : Interrupt Indexes number
 **************************************************************************** */
LITE_OS_SEC_TEXT_MINOR INT32 HalIntNumGet(VOID)
{
    unsigned int pending;
    int n = 0;
    unsigned int intstatus, irq;

    
    pending = read_c0_cause() & read_c0_status() & ST0_IM;

    if (pending & CAUSEF_IP7)
    {
        return LS1C_TICK_IRQ;
    }
    else if (pending & CAUSEF_IP2)
    {
        n = 0;
    }
    else if (pending & CAUSEF_IP3)
    {
        n = 1;
    }
    else if (pending & CAUSEF_IP4)
    {
        n = 2;
    }
    else if (pending & CAUSEF_IP5)
    {
        n = 3;
    }
    else if (pending & CAUSEF_IP6)
    {
        n = 4;
    }else{
        return 0xFFFF;
    }

    /* Receive interrupt signal, compute the irq */
    intstatus = (mips_hw0_icregs+n)->int_isr & (mips_hw0_icregs+n)->int_en;
    if (0 == intstatus)
        return 0xFFFF;

    // 执行中断处理函数
    irq = ffs(intstatus) - 1;

    return (n<<5) + irq;

}

/* ****************************************************************************
 Function    : HalInterrupt
 Description : Hardware interrupt entry function
 Input       : None
 Output      : None
 Return      : None
 **************************************************************************** */
LITE_OS_SEC_TEXT VOID __HalInterrupt(int irqnum, VOID *parm)
{

    if(irqnum == 0xFFFF)
    {
        return ;
    }

    HalPreInterruptHandler(irqnum);
#if (OS_HWI_WITH_ARG == 1)
    if (g_hwiHandlerForm[irqnum + OS_SYS_VECTOR_CNT].pfnHandler != 0) {
        g_hwiHandlerForm[irqnum + OS_SYS_VECTOR_CNT].pfnHandler(irqnum, (VOID *)g_hwiHandlerForm[hwiIndex].pParm);
    }
#else
    if (g_hwiHandlerForm[irqnum + OS_SYS_VECTOR_CNT] != 0) {
        g_hwiHandlerForm[irqnum + OS_SYS_VECTOR_CNT](irqnum, NULL);
    }
#endif

    HalAftInterruptHandler(irqnum);

}


LITE_OS_SEC_TEXT VOID HalInterrupt(VOID)
{
    INT32 hwiIndex = 0xFFFF;

    unsigned int pending;
    int n = 0;
    unsigned int intstatus, irq = 0;
    
    pending = read_c0_cause() & read_c0_status() & ST0_IM;

    if (pending & CAUSEF_IP7)
    {
        hwiIndex = LS1C_TICK_IRQ;
        goto __tick;
    }
    else if (pending & CAUSEF_IP2)
    {
        n = 0;
    }
    else if (pending & CAUSEF_IP3)
    {
        n = 1;
    }
    else if (pending & CAUSEF_IP4)
    {
        n = 2;
    }
    else if (pending & CAUSEF_IP5)
    {
        n = 3;
    }
    else if (pending & CAUSEF_IP6)
    {
        n = 4;
    }else{
        return ;
    }

    /* Receive interrupt signal, compute the irq */
    intstatus = (mips_hw0_icregs+n)->int_isr & (mips_hw0_icregs+n)->int_en;
    if (0 == intstatus)
        return ;

    // 执行中断处理函数
    irq = ffs(intstatus) - 1;

    if(hwiIndex != LS1C_TICK_IRQ)
    {
        hwiIndex = (n<<5) + irq;
    }


__tick:

    if(hwiIndex == 0xFFFF)
    {
        return ;
    }
    
    if(g_hwiForm[hwiIndex + OS_SYS_VECTOR_CNT] != NULL)
    {
        g_hwiForm[hwiIndex + OS_SYS_VECTOR_CNT](hwiIndex, NULL);
    }

    if(hwiIndex != LS1C_TICK_IRQ)
    {
        /* ack interrupt */
        (mips_hw0_icregs+n)->int_clr |= (1 << irq);
    }
}


// 使能中断
UINT32 HalC0PGlobalEnable(VOID)
{

    UINT32 status = 0;
    status = read_c0_status();
    status |= (STATUSF_IP6 | STATUSF_IP5 | STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 | ST0_IE);
    status &= (~STATUSF_IP7);       // 上电后默认禁止sys tick定时器
    write_c0_status(status);
    return status;

    
}

// 禁止中断:设置协处理器0
UINT32 HalC0PGlobalDisable(VOID)
{

    UINT32 status = 0;
    UINT32 disStatus = 0;
    status = read_c0_status();
    disStatus &= 0xfffffffe;
    write_c0_status(disStatus);
    return status;

}

UINT32 HalIntLock(VOID)
{
    UINT32 ret;
    ret = HalC0PGlobalDisable();
    return ret;
}

VOID HalIntRestore(UINT32 intSave)
{
    write_c0_status(intSave);
}

UINT32 HalIntUnLock(VOID)
{
    UINT32 intSave = 0;
    intSave = HalC0PGlobalEnable();
    return intSave;
}

#define MIPS_EXCEPTION_RAM_EBASE                     (0x80000000)

void mips_irq_set_exception_vector_handler(unsigned long offset, void *src_addr, unsigned long size)
{
    unsigned long dst_addr;   // 异常入口

    dst_addr = MIPS_EXCEPTION_RAM_EBASE+offset;
    memcpy((void *)dst_addr, src_addr, size);

    // 先回写dcache，再作废icache
    // memcpy之后，现在异常向量总入口的指令位于dcache中，需要回写到内存，
    // 并作废相应icache，作废后当有中断发生时，才会从内存重新加载新代码到icache，这样新代码就生效了
    dcache_writeback_invalidate_range(dst_addr, dst_addr + size);
    icache_invalidate_range(dst_addr, dst_addr + size);

    return ;
}

void mips_irq_set_one_exception_handler(int n, void *addr)
{
    unsigned long handler = (unsigned long)addr;
    mips_exception_handlers[n] = handler;

    return ;
}

VOID SysTick_Handler(int irqnum, VOID *parm)
{

}

/* ****************************************************************************
 Function    : HalHwiInit
 Description : initialization of the hardware interrupt
 Input       : None
 Output      : None
 Return      : None
 **************************************************************************** */
LITE_OS_SEC_TEXT_INIT VOID HalHwiInit()
{
    int i;
    volatile struct mips_intc_regs *intc_regs = NULL;
    
    // 禁止中断:设置协处理器0
    //HalC0PGlobalDisable();

    // 探测cache类型和大小
    cache_probe();

    // 禁止中断:设置龙芯1C里面的中断配置寄存器
    for (i=0; i<5; i++)     // 龙芯1c的中断分为五组
    {
        intc_regs = mips_hw0_icregs+i;
        intc_regs->int_en   = 0x0;          // disable
        intc_regs->int_pol  = -1;           // must be done here
        intc_regs->int_edge = 0x00000000;   // 电平触发
        intc_regs->int_clr  = 0xffffffff;   // 清中断
    }
    
    // 设置整个异常向量的处理函数
    mips_irq_set_exception_vector_handler(0x180, &mips_general_exception, MIPS_EXCEPTION_HANDER_MAX_SIZE);
    mips_irq_set_exception_vector_handler(0x200, &mips_general_exception, MIPS_EXCEPTION_HANDER_MAX_SIZE);

    // 设置各个异常的处理函数
    for (i=0; i<MIPS_EXCEPTION_MAX_NUM; i++)
    {
        mips_irq_set_one_exception_handler(i, mips_handle_reserved);
    }
    mips_irq_set_one_exception_handler(0, mips_handle_int);

    // 先回写整个dcache，再作废整个icache
    dcache_writeback_invalidate_all();
    icache_invalidate_all();

    /*
     * 设置cp0的寄存器status中的BEV为0
     * 使CPU的异常向量入口为0x80000180
     */
	MIPS_CPU_SetSR(0, SR_BOOT_EXC_VEC);

    // 使能中断
    //HalC0PGlobalEnable();

#if (LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT == 1)
    UINT32 index;

    for (index = 0; index < OS_VECTOR_CNT; index++) { /* 2: The starting position of the interrupt */
        g_hwiForm[index] = (HWI_PROC_FUNC)HalHwiDefaultHandler;
    }
    /* Exception handler register */
    //g_hwiForm[LS1C_TICK_IRQ + OS_SYS_VECTOR_CNT]   = SysTick_Handler;

#endif


    return ;
}


#define FAULT_STATUS_REG_BIT            32
#define USGFAULT                        (1 << 18)
#define BUSFAULT                        (1 << 17)
#define MEMFAULT                        (1 << 16)
#define DIV0FAULT                       (1 << 4)
#define HARDFAULT_IRQN                  (-13)

ExcInfo g_excInfo = {0};

UINT8 g_uwExcTbl[FAULT_STATUS_REG_BIT] = {
    0, 0, 0, 0, 0, 0, OS_EXC_UF_DIVBYZERO, OS_EXC_UF_UNALIGNED,
    0, 0, 0, 0, OS_EXC_UF_NOCP, OS_EXC_UF_INVPC, OS_EXC_UF_INVSTATE, OS_EXC_UF_UNDEFINSTR,
    0, 0, 0, OS_EXC_BF_STKERR, OS_EXC_BF_UNSTKERR, OS_EXC_BF_IMPRECISERR, OS_EXC_BF_PRECISERR, OS_EXC_BF_IBUSERR,
    0, 0, 0, OS_EXC_MF_MSTKERR, OS_EXC_MF_MUNSTKERR, 0, OS_EXC_MF_DACCVIOL, OS_EXC_MF_IACCVIOL
};

#if (LOSCFG_KERNEL_PRINTF != 0)
 VOID OsExcNvicDump(VOID)
{

}

 VOID OsExcTypeInfo(const ExcInfo *excInfo)
{
}

 VOID OsExcCurTaskInfo(const ExcInfo *excInfo)
{

}

 VOID OsExcRegInfo(const ExcInfo *excInfo)
{

}

 VOID OsExcBackTraceInfo(const ExcInfo *excInfo)
{

}

 VOID OsExcMemPoolCheckInfo(VOID)
{

}
#endif

 VOID OsExcInfoDisplay(const ExcInfo *excInfo)
{
}

LITE_OS_SEC_TEXT_INIT VOID HalExcHandleEntry(UINT32 excType, UINT32 faultAddr, UINT32 pid, EXC_CONTEXT_S *excBufAddr)
{

}

/* stack protector */
WEAK UINT32 __stack_chk_guard = 0xd00a0dff;

WEAK VOID __stack_chk_fail(VOID)
{

}


inline UINT32 HalIsIntActive(VOID)
{
    return (g_intCount > 0);
}
